
#ifndef _MEMBER_BTextEdit_H_
#define _MEMBER_BTextEdit_H_

#include <time.h>
#include <BMap>
#include <BColor>
#include <BRect>
#include <BClock>
#include <BPoint>
#include <BString>
#include <BStaticArray>
#include <BRange>
#include <BTextEdit>
#include "member_BFont.h"

namespace BWE
{
	class CodeCell
	{
	public:
		inline CodeCell()
		{
			code = 0;
			image = 0;
			image_left = 0;
			image_top = 0;
		}
		inline CodeCell(int x, int y, int w, int h)
		{
			rect.set(x, y, w, h);
			code = 0;
			image = 0;
			image_left = 0;
			image_top = 0;
		}
		inline CodeCell(const CodeCell& other)
		{
			code = other.code;
			rect = other.rect;
			image = other.image;
			image_left = other.image_left;
			image_top = other.image_top;
			image_rect = other.image_rect;
		}

		inline void fresh(const BFont* font, int rowHeight)
		{
			if (font)
			{
				BSize size = font->codeSize(code);
				rect.setSize(size.width(), rowHeight);

				CodeInfo* info = font_member(font)->getCodeInfo(code);
				if (info && info->bitmap)
				{
					const BImage* img = info->bitmap;
					image = img;
					image_left = info->left;
					image_top = info->top;
					image_rect.setSize(img->width(), img->height());
				}
			}
			else
			{
				rect.setSize(3, rowHeight);
				image = 0;
				image_left = 0;
				image_top = 0;
				image_rect.setSize(0, 0);
			}
		}
		inline bool isSpace() const
		{
			return code == ' '
				|| code == '\t'
				|| code == '\n'
				|| code == '\r'
				|| code == '\v';
		}

		inline bool operator == (BCode code)
		{
			return this->code == code;
		}
		inline bool operator != (BCode code)
		{
			return this->code != code;
		}

		BCode			code;
		BRect			rect;
		int				image_left;
		int				image_top;
		BRect			image_rect;
		const BImage*	image;
	};
	class TextBlock
	{
	public:
		TextBlock();
		TextBlock(const TextBlock& other);
		~TextBlock();

		void reset(const BString& text);
		void append(const BString& text);
		void insert(int pos, BCode code);
		void insert(int pos, const BString& text);
		void refresh(BFont* font);
		void adjust(int width);

		int wordWidth(int index) const;

		int tail() const;

		bool				dirty;
		int					line;
		BString				lineText;
		int					rowHeight;
		BFontHolder			font;
		BRect				rect;
		BArray<CodeCell>	cells;
	};

	typedef BList<TextBlock>::iterator TextBlockIterator;

	class member_BTextEdit
	{
	public:
		member_BTextEdit(BTextEdit* textEdit);
		~member_BTextEdit();

		void initBlocks();
		void appendBlocks(const BString& str);
		void adjustBlocks();
		void updateHover(const BPoint& pos);
		void updateLineNumbers();

		void selectWord();
		void selectBlocks();
		void unselected();

		bool seekPrevWord();
		bool seekNextWord();
		bool seekPrev();
		bool seekNext();
		bool remove();
		bool input(BCode code);
		bool input(const BString& text);

		void paintSelection(BPainter& painter);
		void paintCursor(BPainter& painter);
		
		BTextEdit*			boss;
		BString				text;
		int					lineHeight;
		bool				lineShown;
		bool				lineWrap;
		clock_t				blinkTick;
		int					blinkTime;
		bool				readOnly;
		bool				changed;

		TextBlockIterator	hoverBlock;
		int					hoverIndex;
		TextBlockIterator	inputBlock;
		int					inputIndex;
		bool				inputShown;

		bool				selected;
		int					pressed;
		BPoint				pressPoint;
		int					pressIndex;
		TextBlockIterator	beginBlock;
		int					beginIndex;
		TextBlockIterator	endBlock;
		int					endIndex;

		TextBlockIterator	showFirst;
		TextBlockIterator	showLast;
		BList<TextBlock>	blocks;
	};
}

#endif